/* @(#)server.c
 * Time-stamp: <2008-10-25 20:03:53 root>
 * Copyright 2008 Julian Qian
 * Author: root@localhost.localdomain
 * Version: $Id: server.c,v 0.0 2008/10/07 14:12:19 root Exp $
 * Keywords: 
 */

#include "common.h"


void Server(){
	int listen_id;
	struct sockaddr_in server_addr, client_addr;

	listen_id = Socket(AF_INET, SOCK_DGRAM, 0);

	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	server_addr.sin_port = htons(SERVER_PORT);
	/* bind the socket */
	Bind(listen_id, (SA *)&server_addr, sizeof(server_addr));

	char hostaddr[MAXLEN];
	get_host_addr(hostaddr);
	err_msg("socket at %s:%d", hostaddr, ntohs(server_addr.sin_port));
	/* listen(listen_id, 5); */
	/* len = sizeof(struct sockaddr); */

	int n = 240*1024;
	Setsockopt(listen_id, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));

	int res;
	socklen_t len;
	char buffer[PROTO_LENG_TMP];
	PHONE_PAIR upp;
	/* init database, arg: 1)devid 2)ip 3)stat */
	devid_t devid;
	ip_t ip;
	stat_t stat = 0;
	spec_t spec;
	KEY pkey;
	VALUE pvar;
	VALUE *last_var;

	last_var = (VALUE *)malloc(sizeof(VALUE));
	
	while(1){
		res = Recvfrom(listen_id, buffer, PROTO_LENG_TMP, 0, (SA *)&client_addr, &len);
		if(res > 0){
			/* BEGINNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN.... */
			/* obtain devid, ip and stat */
			ip = ntohl(client_addr.sin_addr.s_addr);
			/* gAddr = ip; */
			devid = get_devid(buffer);
			spec = get_spec(buffer);
			stat = get_stat(buffer);
			
			err_msg("RECEIVED devid:%u,ip:%s",devid, inet_ntoa((struct in_addr)client_addr.sin_addr));
			print_hex(buffer,res);

			/* db_stmt_query(stmt); */
			/* devid = 671088640; */
			/* ip = 4234567890; */
			/* ip = 2840713683; /\* ytxy *\/ */
			
			if(res == PROTO_LENG){

				if(FALSE == auth_devid(devid)) {
					err_msg("Unauthorization: %u", devid);
					continue;
				}

				if(ip == 0){
					err_msg("invalid ip");
					continue;
				}
				push_heart(devid);

				pvar.ip = ip;
				pvar.stat = stat;
				pvar.up_time = (int)time(NULL);
				
				push_hash(&pkey, &pvar, last_var);

				printf("last stat: %d, curr: %d\n", last_var->stat, stat);
				
				/* iff status changed, update database */
				if(last_var->stat != stat || last_var->ip != ip ){
					gDBQ.update_ip = ip;
					gDBQ.update_devid = devid;

					gDBQ.update_stat = stat;
				
					gDBQ.insert_ip = ip;
					gDBQ.insert_spec = spec;
					gDBQ.insert_devid = devid;
					gDBQ.insert_stat = stat;
				
					/* init data into database */
					mutex_lock(gDBQ.mutex);
					db_stmt_query(gDBQ.update_stmt);
					err_msg("stmt_UPDATE devid: %u, ip: %s, stat: %d", devid, IP(ip), gDBQ.update_stat);
					if( ! db_stmt_affected(gDBQ.update_stmt) ) {
						/* insert a record */
						err_msg("stmt_INSERT devid: %u, ip: %u", devid, ip);
						db_stmt_query(gDBQ.insert_stmt);
					}
					mutex_unlock(gDBQ.mutex);					
				}

				switch(buffer[PROTO_TYPE]){
				case CLI_RESET:
					set_stat(buffer, SER_RESET, buffer[PROTO_DATA]);
					Sendto(listen_id, buffer, PROTO_LENG, 0, (struct sockaddr *)&client_addr, len);					
					break;
				case CLI_ALARM:
					set_stat(buffer, SER_ALARM, 0x0);
					if( PROTO_LENG == Sendto(listen_id, buffer, PROTO_LENG, 0, (struct sockaddr *)&client_addr, len) ) {
						/* alarm to phone */
						gDBQ.select_devid = devid;
						mutex_lock(gDBQ.mutex);
						if(!db_stmt_query(gDBQ.select_stmt)){
							db_stmt_phone(&gDBQ, upp.phone);
						}
						mutex_unlock(gDBQ.mutex);
						/* db_query_phone(&gDBConn, devid, phone); */
						err_msg("ALARM to phone num:%s<---", upp.phone);

						if(buffer[PROTO_DATA] == ALARM_MANUA){
							upp.alarm = ALARM_MANUA_USER;
						}else if(buffer[PROTO_DATA] == ALARM_DOORR){
							upp.alarm = ALARM_DOORR_USER;
						}else{
							upp.alarm = ALARM_INFRA_USER;
						}
						/* should be another pthread */

						/*
						pthread_t alarm_pid;
						int alarm_ret = pthread_create(&alarm_pid, NULL, (void *)alarm_user_thread, (PHONE_PAIR *)&upp);
						if( 0 == alarm_ret ) {
							pthread_join(alarm_pid, NULL);
						}
						*/
						ws_alarm(&upp);
					}
					
					break;
				case CLI_TIMER:
					break;
				case CLI_DEFEN:
					/*
					switch(buffer[PROTO_DATA]){
					case CTRL_DEFEN:
						stat = STAT_DEFEN;
						break;
					case CTRL_UNDEF:
						stat = STAT_UNDEF;
						break;
					}
					*/
					set_stat(buffer, SER_DEFEN, 0x0);
					Sendto(listen_id, buffer, PROTO_LENG, 0, (struct sockaddr *)&client_addr, len);

					/*
					gDBQ.update_stat_stat = stat;
					gDBQ.update_stat_devid = devid;
					
					mutex_lock(gDBQ.mutex);
					db_stmt_query(gDBQ.update_stat_stmt);
					mutex_unlock(gDBQ.mutex);
					*/
					break;
				}
			}
		}
	}

	free(last_var);
	close(listen_id);
}

/* update hash table */
int push_hash(KEY *kvar, VALUE *vvar, VALUE *lvar ){
	KEY *k;
	VALUE *v, *found;
	/* stat_t last_stat = STAT_NULLL; */

	memset(lvar, 0, sizeof(VALUE));
	
	k = (KEY*) malloc(sizeof(KEY));
	memcpy(k, kvar, sizeof(KEY));

	v = (VALUE*) malloc(sizeof(VALUE));
	memcpy(v, vvar, sizeof(VALUE));

	if(NULL == ( found = (VALUE*)search_some(gHash, k))){
		err_msg("NEW!!not found! id: %d, ip: %u, stat: %d", k->devid, v->ip, v->stat);

		if( !insert_some(gHash,k,v) ) {
			err_msg("insert hash error!");
			free(k);
			free(v);
		}
		return -1;
	}else{
		/* found it */
		/* err_msg("FOUND here! %ld --> %u, %d", k->devid, found->ip, found->stat); */
		/* execpt heart timer */
		/*
		if(v->stat == STAT_TIMER){
			v->stat = found->stat;
		}
		*/

		memcpy(lvar, found, sizeof(VALUE));

		if( !hashtable_change(gHash, k, v) ) {
			err_msg("update hash error!");
			free(k);
			free(v);
		}
		return 0;
	}
}

